home *** CD-ROM | disk | FTP | other *** search
- /*
- ***********************************************************************
- *
- * MountVol interception to find and display a MOTD file
- *
- * The purpose of this stuff is to detect when a new volume is being
- * mounted (in any possible way: by inserting a floppy, using the Chooser, or
- * clicking on the alias). If a new volume is detected, we check to see if
- * there is a Message-of-the-day file at some "standard" location on that volume.
- * If there is, we get the TeachTech (or whatever other application-creator
- * of the MOTD file) to show the file to the user.
- * At the start-up, we do the similar MOTD seeking thing for each mounted volume.
- *
- * The way we go about this, is patching the _MountVol system trap. The _MountVol
- * is entered whenever the system needs to mount a volume. Our patch saves
- * the driver ref number for the drive that contains the volume. Our main
- * application dozes in the background mode, but wakes up from time to time
- * to check to see if a new volume has been mounted since the last check.
- * If it has, the application gets the TeachTech to show the MOTD file
- * (if it can be located, of course).
- *
- * The path to the MOTD file is expected to be specified as a 'STR ' resource
- * named 'MOTD Path'. Note, the path name should be a partial file name: for
- * obvious reasons, the path name to the MOTD file should not contain the
- * volume name (because it is searched on several volumes). So, the path
- * name should always start with a colon, for example
- * :PUBLIC:Notices:Motd.MAC
- * Make sure that the TeachTech is the creator of the MOTD file (you can use
- * any other editor/viewer, if you wish so).
- *
- ***********************************************************************
- */
-
- /* MacHeaders Included */
- #include "myenv.h"
- #include <string.h>
- #include <AppleEvents.h>
-
- /*
- *----------------------------------------------------------------------
- * Installing our _MountVol interceptor
- * Note, the interceptor is a CODE resource, compiled separately. It
- * is assumed to be put into the resource fork of the application. See
- * the file "MountVol init.c" for more details.
- * Note, the resource has to be loaded in the system heap, and be protected
- * and locked from the outset (because it is a system "patch").
- *
- * Attention! Don't use 'NSetTrapAddress()' to set up the trap address.
- * It works, but it does more than it's supposed to: besides setting the trap
- * address to the new trap handler (our interceptor), it sets some flags in
- * the process context. So, when the process manager backgrounds our application, it
- * _RESTORES_ all the system traps the application has changed to their
- * default values! But we need our interceptor to run even when the application
- * is in the background!
- */
-
- // Sets up a new handler for the specified
- // system trap.
- // It's a brute force method (messing directly
- // with the system trap table), but it seems to
- // be the only way to be sure the handler we
- // installed sticks (even when the application
- // is switched to the background).
- static void my_set_system_trap(ProcPtr handler, const int trap_word)
- {
- assure( trap_word & 0x8ff, "The trap word looks like a toolbox, not a system trap");
- ProcPtr * dispatch_table = (ProcPtr *)0x400; // System Trap dispatch table
- dispatch_table[trap_word & 0xff] = handler;
- // Make sure we really have set the trap
- assert( (ProcPtr)NGetTrapAddress(trap_word & 0xff,OSTrap) == handler );
- }
-
- // "Identification" of our interceptor
- // (code resource)
- #define Interceptor_type 'Init' // Code resource type
- #define Interceptor_name "\pMyMountVol" // Code resource name
- #define Interceptor_sig 23547 // Interceptor's signature
-
- #define _MountVol 0xA00F // _MountVol trap
-
- struct MyMountVolLayout
- {
- short bra_instr; // Entry point: jump around data instruction
- short signature; // To make sure it is what it is
- short driver_ref_no; // Intercepted driver ref number
- ProcPtr real_trap_handler; // Ptr to the real trap program
- }; // The rest is the code for the "interception", of
- // no interest to us here
-
- class MountVolIntercept
- {
- MyMountVolLayout ** interceptor;
- short last_mounted_drive; // Drive ref number for the last mounted drive
- // detected
-
- public:
- MountVolIntercept(void);
- ~MountVolIntercept(void);
- Boolean has_caught(void);
- short last_driver_ref_no(void) const { return last_mounted_drive; }
- };
-
-
- // Install the interceptor
- MountVolIntercept::MountVolIntercept(void)
- {
- // Load our interceptor and make sure we
- // got what we need
- interceptor = (MyMountVolLayout **)Get1NamedResource(Interceptor_type,Interceptor_name);
- assert( interceptor != nil );
- int interceptor_attr = GetResAttrs((Handle)interceptor);
- do_well( ResError() );
- assert( interceptor_attr & resSysHeap ); // Make sure the resource is locked,
- assert( interceptor_attr & resLocked ); // protected, and in the sytem heap
- assert( interceptor_attr & resProtected );
- assert( (*interceptor)->signature == Interceptor_sig );
- // Since we're going to deinstall the interceptor
- // when we quit, we don't need to detach the resource
-
- (*interceptor)->real_trap_handler = (ProcPtr)NGetTrapAddress(_MountVol & 0xff,OSTrap);
- my_set_system_trap((ProcPtr)*interceptor,_MountVol);
-
- last_mounted_drive = 0;
- }
-
- // Uninstall the interceptor and clean up
- // after ourselves
- MountVolIntercept::~MountVolIntercept(void)
- {
- assert( interceptor != 0 && *interceptor != 0 );
- my_set_system_trap((*interceptor)->real_trap_handler,_MountVol);
-
- ReleaseResource((Handle)interceptor);
- // DisposeHandle((Handle)interceptor); <-- use this only if resource was detached
- notify_and_wait("Quitting: _MountVol trap is restored");
- }
-
- // Check to see if the interceptor has caught
- // smth. If it has, save the intercepted drive
- // ref number.
- Boolean MountVolIntercept::has_caught(void)
- {
- short& caught_ref_no = (*interceptor)->driver_ref_no;
- if( caught_ref_no == 0 )
- return FALSE;
-
- last_mounted_drive = caught_ref_no;
- caught_ref_no = 0;
- return TRUE;
- }
-
- /*
- *----------------------------------------------------------------------
- * A class that takes care of displaying the message
- * of the day file
- * First, we try to locate the file at the "standard" location on a given
- * volume. If we got it, we use the 'open_selection()' function to "double-click"
- * on the file by sending a message to the Finder. He does the rest.
- */
-
-
- class DisplayMOTD
- {
- StringPtr * path_name_handle;
- FSSpec motd_fsspec;
-
- public:
- DisplayMOTD(void);
- ~DisplayMOTD(void) {}
- void display_on_volume(const short drive_ref_no);
- };
-
- // Figure out the "standard" location of the
- // MOTD file (from the 'STR ' resource)
- DisplayMOTD::DisplayMOTD(void)
- {
- const unsigned char * path_rsrc_name = "\pMOTD Path";
- path_name_handle = (StringPtr *)Get1NamedResource('STR ',path_rsrc_name);
- assert( path_name_handle != nil );
- assure( (*path_name_handle)[1] == ':', "The 'MOTD Path' should begin with a colon");
- }
-
- void open_selection(const FSSpec& file_spec);
-
- // Find the MOTD file on a given volume and
- // "open_select" it.
- void DisplayMOTD::display_on_volume(const short drive_ref_no)
- {
- Str255 volume_name;
- short vRefNum;
- long free_bytes;
- OSErr err;
- if( (err = GetVInfo(drive_ref_no,volume_name,&vRefNum,&free_bytes)) != noErr )
- {
- notify("Error %d getting the volume info for the drive #%d",err,drive_ref_no);
- return;
- }
-
- if( FSMakeFSSpec(vRefNum,0,*path_name_handle,&motd_fsspec) != noErr )
- {
- // notify("MOTD file was not found on the volume %#s",volume_name);
- return;
- }
-
- open_selection(motd_fsspec);
- }
-
- /*
- *----------------------------------------------------------------------
- * Where everything happens - THE event loop
- */
-
- // Handle a Quit event
- // For now, pretend that we handled it
- // and everything went fine
- static pascal OSErr Handle_AppleEvent_quit
- (AppleEvent * event, AppleEvent * reply, long refcon)
- {
- return noErr;
- }
-
- // Check out already mounted volumes to see
- // if they contain MOTD
- // Note drive_ref_numbers for mounted volumes
- // don't have to be consecutive
- static void check_already_mounted_vols(DisplayMOTD& motd_object)
- {
- const int max_mounted_vols = 10; // Check first few drives
- register int drive_ref_no;
- Str255 volume_name;
- short vRefNum;
- long free_bytes;
- OSErr err;
-
- for(drive_ref_no=1; drive_ref_no<=max_mounted_vols; drive_ref_no++)
- if( (err=GetVInfo(drive_ref_no,volume_name,&vRefNum,&free_bytes)) == noErr )
- motd_object.display_on_volume(drive_ref_no);
- }
-
- void main(void)
- {
- Initialize_MAC();
- // Claim that we can handle a Quit event
- do_well( AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
- (ProcPtr)Handle_AppleEvent_quit,0,FALSE) );
-
- DisplayMOTD motd_object;
- check_already_mounted_vols(motd_object);
- // motd_object.display_on_volume(3);
- // notify_and_wait("Done");
-
- MountVolIntercept intercept_object;
-
- const long event_timeout = 20;
- EventRecord theEvent;
-
- for(;;)
- {
- WaitNextEvent(everyEvent, &theEvent,event_timeout,nil);
- switch (theEvent.what)
- {
- case nullEvent:
- if( intercept_object.has_caught() )
- motd_object.display_on_volume(intercept_object.last_driver_ref_no());
- continue;
-
- case keyDown: // Terminate if any key was pressed
- break;
-
- case 23: // it is nothing but 'kHighLevelEvent'
- OSErr err;
- if( (err=AEProcessAppleEvent(&theEvent)) == noErr )
- break; // It was a Quit event - the only one which
- else if( err != errAEEventNotHandled ) // could be handled
- notify("Error %d in processing an Apple Event",err);
- continue; // NotHandled events are just ignored
-
- default: // Ignore all other events
- continue;
- }
- break;
- }
- }
-